# -*- coding: utf-8 -*-
"""
虚拟网盘服务
@api-version: 1.0.0
// REF: API-NETDISK-009
"""

import os
import json
import hashlib
import mimetypes
import time
from datetime import datetime, timedelta
from typing import Optional, Dict, Any, List, BinaryIO
from werkzeug.datastructures import FileStorage
from flask import current_app

from backend.models import db
from backend.models.virtual_file import VirtualFile
from backend.models.virtual_storage import VirtualStorage, MediaType
from backend.models.file_share import FileShare
from backend.models.user_storage_quota import UserStorageQuota
from backend.models.file_operation_log import FileOperationLog, OperationType
from backend.services.cloud_storage import get_default_adapter
from backend.utils.api_response import ApiResponse

class VirtualNetdiskService:
    """虚拟网盘服务
    
    提供文件上传、下载、管理等核心功能
    支持云存储集成和文件去重
    """
    
    def __init__(self):
        """初始化虚拟网盘服务"""
        # 延迟初始化云存储适配器
        self._cloud_adapter = None
        self.chunk_size = 8192  # 默认分块大小
        self.cloud_provider = 'local'  # 默认云存储提供商
    
    @property
    def cloud_adapter(self):
        """获取云存储适配器（延迟初始化）"""
        if self._cloud_adapter is None:
            self._cloud_adapter = get_default_adapter()
        return self._cloud_adapter
    
    def _get_cloud_adapter(self):
        """获取云存储适配器（兼容方法）"""
        return self.cloud_adapter
    

    
    def _calculate_file_md5(self, file_stream: BinaryIO) -> str:
        """计算文件MD5哈希值"""
        md5_hash = hashlib.md5()
        file_stream.seek(0)
        
        while chunk := file_stream.read(self.chunk_size):
            md5_hash.update(chunk)
        
        file_stream.seek(0)
        return md5_hash.hexdigest()
    
    def _get_media_type_from_filename(self, filename: str) -> MediaType:
        """根据文件名获取媒体类型"""
        ext = os.path.splitext(filename)[1].lower()
        return VirtualStorage.get_media_type_by_extension(ext)
    
    def check_instant_upload(self, user_id: int, file_md5: str, filename: str, 
                           file_size: int, parent_id: int = None) -> ApiResponse:
        """检查文件是否可以秒传
        
        Args:
            user_id: 用户ID
            file_md5: 文件MD5值
            filename: 文件名
            file_size: 文件大小
            parent_id: 父目录ID
        
        Returns:
            ApiResponse: 检查结果
        """
        try:
            # 1. 验证用户配额
            quota = UserStorageQuota.get_or_create(user_id)
            if not quota.can_upload(file_size):
                return ApiResponse.error("存储配额不足", code=413)
            
            # 2. 检查文件是否已存在（可以秒传）
            existing_storage = VirtualStorage.query.filter_by(file_id=file_md5).first()
            
            if existing_storage:
                # 可以秒传：直接创建虚拟文件引用（使用带重试的方法）
                virtual_file = self._create_virtual_file_with_retry(
                    user_id=user_id,
                    name=filename,
                    parent_id=parent_id,
                    storage_file_id=file_md5,
                    file_size=existing_storage.file_size_bytes
                )
                
                # 增加引用计数
                existing_storage.increment_reference()
                
                # 更新用户配额
                quota.add_file(existing_storage.file_size_bytes)
                
                # 提交事务
                db.session.commit()
                
                # 记录操作日志
                FileOperationLog.log_upload(
                    user_id=user_id,
                    file_id=virtual_file.id,
                    file_size=existing_storage.file_size_bytes,
                    target_path=virtual_file.get_full_path(),
                    operation_detail=json.dumps({'is_instant': True, 'method': 'md5_check'})
                )
                
                return ApiResponse.success({
                    'canInstant': True,
                    'fileId': virtual_file.id,
                    'storageFileId': file_md5,
                    'referenceCount': existing_storage.reference_count,
                    'message': '文件秒传成功'
                })
            else:
                # 无法秒传
                return ApiResponse.success({
                    'canInstant': False,
                    'message': '文件不存在，需要上传实际文件'
                })
                
        except Exception as e:
            db.session.rollback()
            return ApiResponse.error(f"检查秒传失败: {str(e)}", code=500)
    
    def upload_file(self, user_id: int, file: FileStorage, parent_id: int = None, 
                   file_md5: str = None, force_upload: bool = False, **kwargs) -> ApiResponse:
        """上传文件
        
        Args:
            user_id: 用户ID
            file: 上传的文件
            parent_id: 父目录ID
            file_md5: 预计算的MD5值（用于秒传）
            **kwargs: 其他参数
        
        Returns:
            ApiResponse: 上传结果
        """
        try:
            # 1. 验证用户配额
            quota = UserStorageQuota.get_or_create(user_id)
            if not quota.can_upload(file.content_length or 0):
                return ApiResponse.error("存储配额不足", code=413)
            
            # 2. 计算文件MD5
            if not file_md5:
                file_md5 = self._calculate_file_md5(file.stream)
            
            # 3. 检查文件是否已存在（秒传）
            existing_storage = VirtualStorage.query.filter_by(file_id=file_md5).first()
            
            if existing_storage:
                # 文件已存在，无论是否force_upload都使用现有存储记录
                # 直接创建虚拟文件引用（使用带重试的方法）
                virtual_file = self._create_virtual_file_with_retry(
                    user_id=user_id,
                    name=file.filename,
                    parent_id=parent_id,
                    storage_file_id=file_md5,
                    file_size=existing_storage.file_size_bytes
                )
                
                # 增加引用计数
                existing_storage.increment_reference()
                
                # 更新用户配额
                quota.add_file(existing_storage.file_size_bytes)
                
                # 记录操作日志
                FileOperationLog.log_upload(
                    user_id=user_id,
                    file_id=virtual_file.id,
                    file_size=existing_storage.file_size_bytes,
                    target_path=virtual_file.get_full_path(),
                    operation_detail=json.dumps({
                        'is_instant': True, 
                        'method': 'file_upload_instant',
                        'force_upload_requested': force_upload
                    })
                )
                
                return ApiResponse.success({
                    'fileId': virtual_file.id,
                    'storageFileId': file_md5,
                    'referenceCount': existing_storage.reference_count,
                    'isInstant': True,
                    'isDuplicate': True,
                    'cloudFileId': existing_storage.cloud_file_id,
                    'fileSize': existing_storage.file_size_bytes,
                    'uploadTime': datetime.now().isoformat(),
                    'message': '文件秒传成功（相同文件已存在）'
                })
            
            # 4. 上传到云存储
            cloud_adapter = self._get_cloud_adapter()
            
            upload_result = cloud_adapter.upload_file(
                file_stream=file.stream,
                file_name=file.filename,
                file_size=file.content_length or 0,
                content_type=file.content_type,
                file_md5=file_md5
            )
            
            if not upload_result.get('success'):
                return ApiResponse.error(
                    f"云存储上传失败: {upload_result.get('error_message')}",
                    code=500
                )
            
            # 5. 创建存储记录（处理并发插入）
            media_type = self._get_media_type_from_filename(file.filename)
            
            try:
                storage = VirtualStorage(
                    file_id=file_md5,
                    original_filename=file.filename,
                    file_size_bytes=upload_result.get('file_size', file.content_length or 0),
                    cloud_file_id=upload_result['cloud_file_id'],
                    cloud_provider=self.cloud_provider,
                    media_type=media_type,
                    file_extension=os.path.splitext(file.filename)[1].lower().lstrip('.')
                )
                
                # 设置下载链接信息
                if upload_result.get('download_url'):
                    storage.cloud_download_url = upload_result['download_url']
                if upload_result.get('expires_at'):
                    storage.url_expires_at = upload_result['expires_at']
                
                db.session.add(storage)
                db.session.flush()  # 获取ID
                
            except Exception as e:
                # 检查是否是重复键错误（并发插入）
                if 'Duplicate entry' in str(e) and 'file_id' in str(e):
                    # 回滚当前事务
                    db.session.rollback()
                    
                    # 查询已存在的存储记录
                    storage = VirtualStorage.query.filter_by(file_id=file_md5).first()
                    if not storage:
                        # 如果仍然找不到记录，重新抛出异常
                        raise Exception(f"并发插入处理失败: {str(e)}")
                    
                    # 使用已存在的存储记录，增加引用计数
                    storage.increment_reference()
                    
                    # 标记为已存在记录，并更新用户配额
                    storage._is_existing_record = True
                    quota.add_file(storage.file_size_bytes)
                else:
                    # 其他异常直接抛出
                    raise e
            
            # 6. 创建虚拟文件（使用带重试的方法）
            virtual_file = self._create_virtual_file_with_retry(
                user_id=user_id,
                name=file.filename,
                parent_id=parent_id,
                storage_file_id=file_md5,
                file_size=storage.file_size_bytes
            )
            
            # 7. 更新用户配额（仅在新创建存储记录时）
            # 如果是并发插入使用已存在记录，配额已在异常处理中更新
            if not hasattr(storage, '_is_existing_record'):
                quota.add_file(storage.file_size_bytes)
            
            # 8. 提交事务
            db.session.commit()
            
            # 9. 记录操作日志
            FileOperationLog.log_upload(
                user_id=user_id,
                file_id=virtual_file.id,
                file_size=storage.file_size_bytes,
                target_path=virtual_file.get_full_path(),
                operation_detail=json.dumps({
                    'cloud_file_id': upload_result['cloud_file_id'],
                    'is_instant': False
                })
            )
            
            # 判断是否为并发秒传
            is_concurrent_instant = hasattr(storage, '_is_existing_record')
            
            return ApiResponse.success({
                'fileId': virtual_file.id,
                'storageFileId': file_md5,
                'referenceCount': storage.reference_count,
                'isInstant': is_concurrent_instant,
                'isDuplicate': upload_result.get('is_duplicate', False) or is_concurrent_instant,
                'cloudFileId': storage.cloud_file_id,
                'fileSize': storage.file_size_bytes,
                'uploadTime': datetime.now().isoformat(),
                'message': '文件秒传成功（并发上传检测）' if is_concurrent_instant else ('文件上传成功' if not upload_result.get('is_duplicate') else '文件已存在，上传成功')
            })
            
        except Exception as e:
            # 检查是否是虚拟文件创建的重复键错误
            if 'Duplicate entry' in str(e) and 'uk_files_name_parent_owner' in str(e):
                # 虚拟文件重复键错误，尝试重新创建虚拟文件
                try:
                    # 重新创建虚拟文件，使用带重试的逻辑
                    virtual_file = self._create_virtual_file_with_retry(
                        user_id=user_id,
                        name=file.filename,
                        parent_id=parent_id,
                        storage_file_id=file_md5,
                        file_size=storage.file_size_bytes
                    )
                    
                    # 提交事务
                    db.session.commit()
                    
                    return ApiResponse.success({
                        'fileId': virtual_file.id,
                        'storageFileId': file_md5,
                        'referenceCount': storage.reference_count,
                        'isInstant': False,
                        'isDuplicate': False,
                        'cloudFileId': storage.cloud_file_id,
                        'fileSize': storage.file_size_bytes,
                        'uploadTime': datetime.now().isoformat(),
                        'message': '文件上传成功（并发冲突已解决）'
                    })
                except Exception as retry_e:
                    db.session.rollback()
                    return ApiResponse.error(f"上传失败（重试后仍失败）: {str(retry_e)}", code=500)
            else:
                db.session.rollback()
                return ApiResponse.error(f"上传失败: {str(e)}", code=500)
    
    def _create_virtual_file(self, user_id: int, name: str, parent_id: int = None,
                           storage_file_id: str = None, file_size: int = 0,
                           is_folder: bool = False) -> VirtualFile:
        """创建虚拟文件记录"""
        # 确保用户有根目录
        if parent_id is None:
            root = VirtualFile.get_user_root(user_id)
            parent_id = root.id
        
        # 并发控制：直接在数据库层面处理重复键错误
        import random
        import time
        
        max_retries = 10
        base_name, extension = os.path.splitext(name)
        tried_names = set()  # 记录已尝试的文件名
        
        attempt = 0
        while attempt < max_retries:
            # 生成候选文件名
            if attempt == 0:
                candidate_name = name
            else:
                # 第一次失败后，直接在文件名后加上时间戳和序号
                timestamp = str(int(time.time() * 1000))  # 毫秒级时间戳
                base_name, extension = os.path.splitext(name)
                candidate_name = f"{base_name}_{timestamp}_{attempt}{extension}"
            
            # 如果已经尝试过这个名称，尝试下一个
            if candidate_name in tried_names:
                attempt += 1
                continue
                
            tried_names.add(candidate_name)
            
            try:
                virtual_file = VirtualFile(
                    name=candidate_name,
                    parent_id=parent_id,
                    is_folder=is_folder,
                    storage_file_id=storage_file_id,
                    file_size=file_size,
                    owner_id=user_id
                )
                
                db.session.add(virtual_file)
                db.session.flush()
                
                return virtual_file
                
            except Exception as e:
                # 检查是否是重复键错误
                if 'Duplicate entry' in str(e) and 'uk_files_name_parent_owner' in str(e):
                    # 回滚当前的添加操作
                    db.session.rollback()
                    # 短暂等待后重试，避免高频冲突
                    time.sleep(0.001 * (attempt + 1))
                    attempt += 1
                    continue
                else:
                    # 其他错误，直接抛出
                    db.session.rollback()
                    raise e
            
            attempt += 1
        
        # 如果所有重试都失败，抛出异常
        raise Exception(f"创建虚拟文件失败，已重试{max_retries}次，文件名冲突无法解决")
    
    def _create_virtual_file_with_retry(self, user_id: int, name: str, parent_id: int = None,
                                      storage_file_id: str = None, file_size: int = 0,
                                      is_folder: bool = False) -> VirtualFile:
        """创建虚拟文件记录（带重试逻辑，独立事务）"""
        # 确保用户有根目录
        if parent_id is None:
            root = VirtualFile.get_user_root(user_id)
            parent_id = root.id
        
        import random
        import time
        
        max_retries = 10
        base_name, extension = os.path.splitext(name)
        tried_names = set()  # 记录已尝试的文件名
        
        attempt = 0
        while attempt < max_retries:
            # 生成候选文件名
            if attempt == 0:
                candidate_name = name
            elif attempt == 1:
                # 第一次重试：添加"-副本"
                candidate_name = f"{base_name}-副本{extension}"
            else:
                # 后续重试：添加序号
                candidate_name = f"{base_name}-副本({attempt}){extension}"
            
            # 如果已经尝试过这个名称，尝试下一个
            if candidate_name in tried_names:
                attempt += 1
                continue
                
            tried_names.add(candidate_name)
            
            # 先检查数据库中是否已存在同名文件
            existing_check = VirtualFile.query.filter_by(
                name=candidate_name,
                parent_id=parent_id,
                owner_id=user_id
            ).first()
            
            if existing_check:
                attempt += 1
                continue
            
            try:
                # 创建虚拟文件对象
                virtual_file = VirtualFile(
                    name=candidate_name,
                    parent_id=parent_id,
                    is_folder=is_folder,
                    storage_file_id=storage_file_id,
                    file_size=file_size,
                    owner_id=user_id
                )
                
                db.session.add(virtual_file)
                db.session.flush()  # 立即检查约束
                
                return virtual_file
                    
            except Exception as e:
                # 检查是否是重复键错误
                if 'Duplicate entry' in str(e) and 'uk_files_name_parent_owner' in str(e):
                    # 短暂等待后重试，避免高频冲突
                    time.sleep(0.001 * (attempt + 1))
                    attempt += 1
                    continue
                else:
                    # 其他错误，直接抛出
                    raise e
        
        # 如果所有重试都失败，抛出异常
        raise Exception(f"创建虚拟文件失败，已重试{max_retries}次，文件名冲突无法解决")
    
    def _resolve_filename_conflict(self, user_id: int, filename: str, parent_id: int, tried_names: set = None) -> str:
        """
        解决文件名冲突，生成新的文件名
        
        Args:
            user_id: 用户ID
            filename: 原始文件名
            parent_id: 父目录ID
            
        Returns:
            str: 解决冲突后的文件名
        """
        base_name, extension = os.path.splitext(filename)
        
        # 查询当前文件夹下所有相同前缀的文件
        like_pattern = f"{base_name}%"
        existing_files = VirtualFile.query.filter(
            VirtualFile.name.like(like_pattern),
            VirtualFile.parent_id == parent_id,
            VirtualFile.owner_id == user_id
        ).all()
        
        existing_names = {f.name for f in existing_files}
        
        # 合并数据库中的文件名和已尝试的文件名
        if tried_names:
            existing_names.update(tried_names)
        
        # 第一步：尝试 "文件名-副本.扩展名"
        copy_name = f"{base_name}-副本{extension}"
        if copy_name not in existing_names:
            return copy_name
        
        # 第二步：分析已存在的文件名，找出最大序号
        import re
        max_number = 1  # 默认从1开始，但1不显示
        pattern = re.compile(rf"^{re.escape(base_name)}-副本(?:\((\d+)\))?{re.escape(extension)}$")
        
        for name in existing_names:
            match = pattern.match(name)
            if match:
                if match.group(1):  # 有括号中的数字
                    number = int(match.group(1))
                    max_number = max(max_number, number)
                else:  # 没有括号，表示是第一个副本（序号1）
                    max_number = max(max_number, 1)
        
        # 找到最小且不存在的序号
        used_numbers = set()
        for name in existing_names:
            match = pattern.match(name)
            if match:
                if match.group(1):  # 有括号中的数字
                    used_numbers.add(int(match.group(1)))
                else:  # 没有括号，表示是第一个副本（序号1）
                    used_numbers.add(1)
        
        # 从2开始找最小的未使用序号
        next_number = 2
        while next_number in used_numbers:
            next_number += 1
            
            # 防止无限循环，设置最大重试次数
            if next_number > 9999:
                # 如果超过9999次重试，使用UUID确保唯一性
                import uuid
                unique_suffix = str(uuid.uuid4())[:8]
                return f"{base_name}-副本-{unique_suffix}{extension}"
        
        return f"{base_name}-副本({next_number}){extension}"
    
    def get_download_url(self, user_id: int, file_id: int) -> ApiResponse:
        """获取文件下载链接"""
        try:
            # 1. 验证文件权限
            virtual_file = VirtualFile.query.filter_by(
                id=file_id, owner_id=user_id
            ).first()
            
            if not virtual_file or virtual_file.is_folder:
                return ApiResponse.error("文件不存在", code=404)
            
            # 2. 获取存储信息
            storage = VirtualStorage.query.filter_by(
                file_id=virtual_file.storage_file_id
            ).first()
            
            if not storage:
                return ApiResponse.error("存储文件不存在", code=404)
            
            # 3. 检查下载链接是否有效
            if storage.is_download_url_valid():
                download_url = storage.cloud_download_url
            else:
                # 重新获取下载链接
                cloud_adapter = self._get_cloud_adapter()
                result = cloud_adapter.get_download_url(storage.cloud_file_id)
                
                if not result.get('success'):
                    return ApiResponse.error(
                        f"获取下载链接失败: {result.get('error_message')}",
                        code=500
                    )
                
                # 更新存储记录
                storage.update_download_url(
                    result['download_url'],
                    result['expires_at']
                )
                download_url = result['download_url']
            
            # 4. 更新访问统计
            virtual_file.increment_access()
            storage.increment_download()
            
            # 5. 记录操作日志
            FileOperationLog.log_download(
                user_id=user_id,
                file_id=file_id,
                file_size=storage.file_size_bytes,
                target_path=virtual_file.get_full_path()
            )
            
            db.session.commit()
            
            return ApiResponse.success({
                'downloadUrl': download_url,
                'fileName': virtual_file.name,
                'fileSize': storage.file_size_bytes,
                'expiresAt': storage.url_expires_at.isoformat() if storage.url_expires_at else None
            })
            
        except Exception as e:
            return ApiResponse.error(f"获取下载链接失败: {str(e)}", code=500)
    
    def list_files(self, user_id: int, parent_id: int = None, 
                  page: int = 1, page_size: int = 20, folders_only: bool = False) -> ApiResponse:
        """获取文件列表"""
        try:
            # 调试日志
            print(f"🔍 服务层参数调试: user_id={user_id}, parent_id={parent_id}, folders_only={folders_only}")
            # 获取用户根目录
            if parent_id is None:
                root = VirtualFile.get_user_root(user_id)
                parent_id = root.id
            
            # 验证父目录权限
            parent = VirtualFile.query.filter_by(
                id=parent_id, owner_id=user_id
            ).first()
            
            if not parent:
                return ApiResponse.error("目录不存在", code=404)
            
            # 分页查询
            query = VirtualFile.query.filter_by(
                parent_id=parent_id, owner_id=user_id
            )
            
            # 如果只查询文件夹，添加过滤条件
            if folders_only:
                query = query.filter(VirtualFile.is_folder == True)
            
            query = query.order_by(VirtualFile.is_folder.desc(), VirtualFile.name)
            
            pagination = query.paginate(
                page=page, per_page=page_size, error_out=False
            )
            
            # 构建文件列表
            items = []
            for file in pagination.items:
                file_data = file.to_dict()
                
                # 添加存储信息
                if not file.is_folder and file.storage_file_id:
                    storage = VirtualStorage.query.filter_by(
                        file_id=file.storage_file_id
                    ).first()
                    
                    if storage:
                        file_data.update({
                            'originalFilename': storage.original_filename,
                            'fileExtension': storage.file_extension,
                            'mediaType': storage.media_type,
                            'downloadCount': storage.download_count,
                            'referenceCount': storage.reference_count,
                            'largeIconUrl': storage.large_icon_url,
                            'smallIconUrl': storage.small_icon_url,
                            'status': storage.status,
                            'isHot': storage.is_hot
                        })
                
                items.append(file_data)
            
            return ApiResponse.success({
                'items': items,
                'total': pagination.total,
                'page': page,
                'pageSize': page_size,
                'totalPages': pagination.pages,
                'parentPath': parent.get_full_path() if parent else '/'
            })
            
        except Exception as e:
            return ApiResponse.error(f"获取文件列表失败: {str(e)}", code=500)
    
    def delete_files(self, user_id: int, file_ids: List[int]) -> ApiResponse:
        """删除文件"""
        try:
            deleted_files = []
            reclaimed_space = 0
            
            for file_id in file_ids:
                # 验证文件权限
                virtual_file = VirtualFile.query.filter_by(
                    id=file_id, owner_id=user_id
                ).first()
                
                if not virtual_file:
                    continue
                
                file_size = virtual_file.file_size
                file_path = virtual_file.get_full_path()
                
                # 先记录操作日志（在删除文件之前）
                try:
                    FileOperationLog.log_delete(
                        user_id=user_id,
                        file_id=file_id,
                        target_path=file_path
                    )
                except Exception as log_error:
                    pass  # 记录操作失败，静默处理
                
                # 递归删除
                self._delete_virtual_file_recursive(virtual_file)
                
                deleted_files.append({
                    'id': file_id,
                    'name': virtual_file.name,
                    'path': file_path,
                    'size': file_size
                })
                
                reclaimed_space += file_size
            
            # 更新用户配额
            if reclaimed_space > 0:
                quota = UserStorageQuota.get_or_create(user_id)
                quota.used_space = max(0, quota.used_space - reclaimed_space)
                quota.updated_at = datetime.utcnow()
            
            db.session.commit()
            
            return ApiResponse.success({
                'deletedFiles': deleted_files,
                'reclaimedSpace': reclaimed_space,
                'message': f'成功删除 {len(deleted_files)} 个文件'
            })
            
        except Exception as e:
            db.session.rollback()
            return ApiResponse.error(f"删除失败: {str(e)}", code=500)
    
    def _delete_virtual_file_recursive(self, virtual_file: VirtualFile):
        """递归删除虚拟文件"""
        if virtual_file.is_folder:
            # 删除子文件
            children = VirtualFile.query.filter_by(parent_id=virtual_file.id).all()
            for child in children:
                self._delete_virtual_file_recursive(child)
        else:
            # 减少存储引用计数
            if virtual_file.storage_file_id:
                storage = VirtualStorage.query.filter_by(
                    file_id=virtual_file.storage_file_id
                ).first()
                
                if storage:
                    storage.decrement_reference()
                    
                    # 如果引用计数为0，删除云端文件
                    if storage.reference_count <= 0:
                        try:
                            cloud_adapter = self._get_cloud_adapter()
                            cloud_adapter.delete_file(storage.cloud_file_id)
                            db.session.delete(storage)
                        except Exception as e:
                            pass  # 删除云文件失败，静默处理
        
        # 删除相关的操作日志记录
        FileOperationLog.query.filter_by(file_id=virtual_file.id).delete()
        
        # 删除虚拟文件记录
        db.session.delete(virtual_file)
    
    def create_folder(self, user_id: int, name: str, parent_id: int = None) -> ApiResponse:
        """创建文件夹"""
        try:
            # 验证父目录
            if parent_id is None:
                root = VirtualFile.get_user_root(user_id)
                parent_id = root.id
            else:
                parent = VirtualFile.query.filter_by(
                    id=parent_id, owner_id=user_id, is_folder=True
                ).first()
                
                if not parent:
                    return ApiResponse.error("父目录不存在", code=404)
            
            # 检查同名文件夹
            existing = VirtualFile.query.filter_by(
                name=name, parent_id=parent_id, owner_id=user_id
            ).first()
            
            if existing:
                return ApiResponse.error("文件夹已存在", code=409)
            
            # 创建文件夹
            folder = self._create_virtual_file_with_retry(
                user_id=user_id,
                name=name,
                parent_id=parent_id,
                is_folder=True
            )
            
            # 更新用户配额
            quota = UserStorageQuota.get_or_create(user_id)
            quota.add_folder()
            
            db.session.commit()
            
            # 记录操作日志
            FileOperationLog.log_operation(
                user_id=user_id,
                operation_type=OperationType.CREATE_FOLDER,
                file_id=folder.id,
                target_path=folder.get_full_path()
            )
            
            return ApiResponse.success({
                'id': folder.id,
                'name': folder.name,
                'fullPath': folder.get_full_path(),
                'createdAt': folder.created_at.isoformat()
            })
            
        except Exception as e:
            db.session.rollback()
            return ApiResponse.error(f"创建文件夹失败: {str(e)}", code=500)
    
    def get_user_storage_info(self, user_id: int) -> ApiResponse:
        """获取用户存储信息"""
        try:
            quota = UserStorageQuota.get_or_create(user_id)
            
            # 重新计算使用情况
            quota.recalculate_usage()
            
            return ApiResponse.success(quota.to_dict())
            
        except Exception as e:
            return ApiResponse.error(f"获取存储信息失败: {str(e)}", code=500)